use core::clone::Clone;
use core::option::Option::None;
use serialport::{self, DataBits, FlowControl, Parity, StopBits};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use tauri::{AppHandle, Emitter, Manager, State};
use umya_spreadsheet;
#[derive(Debug, serde::Serialize)]
struct SnValid {
    sn: String,
    valid: String,
}

#[tauri::command]
fn read_excel_column(filename: String) -> Result<Vec<SnValid>, String> {
    let book = umya_spreadsheet::reader::xlsx::read(&filename).unwrap();
    let sheet = book.get_sheet_by_name("Sheet1").unwrap();
    let rows = sheet.get_collection_by_column(&1);

    let mut result: Vec<SnValid> = Vec::new();
    let row_count = rows.len();
    println!("row_count: {}", row_count);
    for i in 1..(row_count + 1) {
        let sn = sheet.get_cell_value((1, i as u32)).get_value();
        let valid = sheet.get_cell_value((2, i as u32)).get_value();

        result.push(SnValid {
            sn: sn.to_string(),
            valid: valid.to_string(),
        });

        println!("sn: {}, valid: {}", sn, valid);
    }

    Ok(result)
}

#[tauri::command]
fn write_excel_cell(filename: String, row_index: u32, value: i32) -> Result<(), String> {
    let mut book = umya_spreadsheet::reader::xlsx::read(&filename).unwrap();
    let sheet = book.get_sheet_by_name_mut("Sheet1").unwrap();

    let v = sheet.get_cell_mut((1, row_index)).get_value();
    println!("v: {}", v);

    sheet.get_cell_mut((2, row_index)).set_value(if value == 1 { "Y" } else { "N" });
   

    match umya_spreadsheet::writer::xlsx::write(&book, &filename) {
        Ok(_) => Ok(()),
        Err(e) => Err(e.to_string()),
    }
}

struct SerialPortManager {
    port: Arc<Mutex<Option<Box<dyn serialport::SerialPort>>>>,
    read_threads: Arc<Mutex<Option<thread::JoinHandle<()>>>>,
    should_exist: Arc<AtomicBool>,
}

impl SerialPortManager {
    fn new() -> Self {
        SerialPortManager {
            // set port to None
            port: Arc::new(Mutex::new(None)),
            // set read_threads to None
            read_threads: Arc::new(Mutex::new(None)),
            should_exist: Arc::new(AtomicBool::new(false)),
        }
    }
}

#[tauri::command]
fn enumerate_ports() -> Vec<String> {
    let ports = serialport::available_ports().unwrap();
    let mut port_names = Vec::new();
    for port in ports {
        port_names.push(port.port_name);
    }
    port_names
}

#[tauri::command]
fn connect_to_port(
    port: String,
    baudrate: u32,
    state: State<'_, SerialPortManager>,
    app_handle: AppHandle,
) -> i32 {
    let instance = match serialport::new(&port, baudrate)
        .timeout(Duration::from_millis(2000)) // Set a reasonable timeout
        .data_bits(DataBits::Eight)
        .stop_bits(StopBits::One)
        .flow_control(FlowControl::None)
        .parity(Parity::None)
        .open()
    {
        Ok(instance) => instance,
        Err(e) => {
            println!("Failed to open serial port: {}", e);
            return -1;
        }
    };

    //set state's port to open a serialport
    {
        let mut port = match state.port.lock() {
            Ok(port) => port,
            Err(e) => {
                println!("Failed to lock port: {}", e);
                return -1;
            }
        };
        *port = Some(instance);
    }

    let app_handle_clone = app_handle.clone();
    let should_exit = state.should_exist.clone();
    // copy port of state
    let read_instance = state.port.clone();

    let read_thread_handle = thread::spawn(move || {
        let mut buffer = [0; 1024];
        loop {
            if should_exit.load(Ordering::SeqCst) {
                break;
            }

            let result = {
                let mut ins = read_instance.lock().unwrap();
                if let Some(ref mut port) = *ins {
                    port.read(&mut buffer)
                } else {
                    Err(std::io::Error::new(
                        std::io::ErrorKind::Other,
                        "Port not open",
                    ))
                }
            }; // The lock is released here when `ins` goes out of scope

            match result {
                Ok(n) if n > 0 => {
                    println!("Received data: {}", String::from_utf8_lossy(&buffer[..n]));
                    app_handle_clone.emit("serial-data", &buffer[..n]).unwrap();
                }
                Ok(_) => {
                    println!("No data received");
                }
                Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => {
                    println!("Read operation timed out, retrying...");
                }
                Err(e) => {
                    println!("Error reading data: {:?}", e);
                    break;
                }
            }

            thread::sleep(Duration::from_millis(300));
        }
    });

    let mut thread_handle = state.read_threads.lock().unwrap();
    *thread_handle = Some(read_thread_handle);
    0
}

#[tauri::command]
fn write_to_port(data: Vec<u8>, state: State<'_, SerialPortManager>) -> usize {
    let mut port_option = match state.port.lock() {
        Ok(port) => port,
        Err(e) => {
            println!("Failed to lock port: {}", e);
            return 0;
        }
    };

    if let Some(ref mut port) = *port_option {
        match port.write_all(&data) {
            Ok(n) => n,
            Err(e) => {
                println!("Error writing data: {}", e);
            }
        };

        data.len()
    } else {
        0
    }
}

#[tauri::command]
fn close_port(state: State<'_, SerialPortManager>) {
    // 设置退出标志
    state.should_exist.store(true, Ordering::SeqCst);

    //drop port
    let mut port_option = match state.port.lock() {
        Ok(port) => port,
        Err(e) => {
            println!("Failed to lock port: {}", e);
            return;
        }
    };

    *port_option = None;

    // wait thread of state
    let mut thread_handle = match state.read_threads.lock() {
        Ok(thread_handle) => thread_handle,
        Err(e) => {
            println!("Failed to lock thread: {}", e);
            return;
        }
    };

    if let Some(thread) = thread_handle.take() {
        thread.join().unwrap();
    }

    // 重置退出标志
    state.should_exist.store(false, Ordering::SeqCst);
}


// remember to call `.manage(MyState::default())`
#[tauri::command]
async fn switch_debug(open: bool, app_handle: AppHandle) -> Result<(), String> {
    let window = app_handle.get_webview_window("main").unwrap();
    println!("open: {}", open);
    if open {
        window.open_devtools();
    } else {
        window.close_devtools();
    }
    Ok(())
}


#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .setup(|app| {
            #[cfg(debug_assertions)]
            {
                // let window = app.get_webview_window("main").unwrap();
                // window.open_devtools();
            }
            Ok(())
        })
        .plugin(tauri_plugin_log::Builder::new().build())
        .plugin(tauri_plugin_opener::init())
        .manage(SerialPortManager::new())
        .invoke_handler(tauri::generate_handler![
            enumerate_ports,
            connect_to_port,
            write_to_port,
            close_port,
            read_excel_column,
            write_excel_cell,
            switch_debug
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
